<?php
//bookstore API helper
defined( '_JEXEC' ) or die( 'Restricted access' );
echo"<!--here text-->";
/**
*  api class for bookstore component
*
*/
class bookstore_api{
	private static $_Instance;
	private $_api_groups = array();
	private $_xmlparams_file;
	private $_config_file;
	private $_jparam;
	private $_registry;
	private $_simplexml;
	private $_api_model_local;
	private $_api_model_service;
	private $_vars = array();
	
	/**
	 * get bookstore_api singleton
	 *
	 * @return object The bookstore_api object 
	 */
	public static function getInstance(){
		if (!self::$_Instance){
			self::$_Instance = new bookstore_api();
			if(!self::$_Instance->registerConfig()){ die('Could not initialize configurations'); };
		}
        return self::$_Instance;
	}
	
	/**
	* constructor
	* TODO: code fail safe if model/view can't be found (should unset api)
	*/
	private function __construct(){
		//get bookstore model file and build bookstore model
		$file = JPATH_ADMINISTRATOR.DS.'components'.DS.'com_bookstore'.DS.'helpers'.DS.'xml_parser.php';
		require_once($file);
		$this->_xmlparams_file = JPATH_ADMINISTRATOR.DS.'components'.DS.'com_bookstore'.DS.'helpers'.DS.'api.xml';
		$this->_config_file = $this->_xmlparams_file;
		$this->_registry =& JRegistry::getInstance('bookstore_api','bookstore_api');
		$file = JPATH_SITE.DS.'components'.DS.'com_bookstore'.DS.'models'.DS.'bookstore.php';		
		if (file_exists( $file )) {
			require_once( $file );
			$this->_api_model_local = new bookstoreModelbookstore();
		}//end iffile exists
		//get external service model file and build the service model				
		$file = JPATH_SITE.DS.'components'.DS.'com_bookstore'.DS.'detail.php';
		if (file_exists( $file )) {
			require_once( $file );
			$this->_api_model_service = new detail_query();
		}//end iffile exists				
		return $this;
	}//end bookstore_api constructor
	
	/**
	 * object factory
	 *
	 * @param string $type object type to create
	 * @param boolean $new attempt to recycle previous object of given type
	 * @param string $data code to inject into object
	 * @return object
	 */
	public static function factory($type = 'simplexml', $new = true, $data = ''){
		$api = self::getInstance();
		$type = '_'.$type;		
		if(!$new){						
			if(!empty($api->$type)){
				return $api->$type;
			}
		}
		switch($type){				
			case '_jparam':			
				$obj = new JParameter($data,$api->_xmlparams_file);
				$obj->addElementPath(JPATH_ADMINISTRATOR.DS.'components'.DS.'com_bookstore'.DS.'elements');
				break;
			case '_registry':
				return $api->_registry;
				break;
			case '_simplexml':
			default:
				$obj = JFactory::getXMLParser('Simple');
				if(strstr($data,array('.DS.','/','\\') ) ){
					$obj->loadFile($data);
				}elseif(!empty($data)){
					$obj->loadString($data);	
				}else{
					$obj->loadFile($api->_xmlparams_file);
				}
				break;
		}
		if(!$new){
			$api->$type = $obj;
		}
		return $obj;
	}
	
	/**
	 * add registry values from XML file
	 * 
	 * called by construct to register basic configs
	 * devs can load files with 2nd arg as path to XML
	 * after loading a config, one could access with:
	 *  "bookstore_api::factory('registry')->getValue('bookstore_api.componentname.node1.node1child');"
	 *
	 * @param string $name Name of component to load
	 * @param string $independent File path to XML
	 * @param boolian $overload Overload previous values
	 * @return boolean
	 */
	private static function registerConfig($name = '',$independent = NULL,$overload = false){
		$api = self::getInstance();
		$file = ($independent)? JFilterInput::clean($independent,'PATH') : $file = $api->_config_file;		
		$regobj = $api->factory('registry');				 
		if(!empty($name) && $regobj->getValue($name) && ($overload == false)){ return true; }		
		$nodes = bookstoreXMLparser::getNodes($file,$name);		
		if(empty($nodes)){return false;}
		foreach($nodes as $node){//register each node
			$obj = bookstoreXMLparser::toObject($node);
			$regobj->loadObject($obj);
		}			
		return true;		
	} 
	
	/**
	*	input JSON string and returns php object
	*	NOTE: probably much more effecient ways to do this:
	*		PECL extension
	*		PEAR extension
	*		native in PHP 5.2
	*/	
	function JSON_Decoder($str = ''){
		if(!empty($str)){
			$obj = new stdclass();
			$str = substr($str,2);
			$str = substr($str,0,-2);
			$variables = explode('","',$str);
			foreach($variables as $value){
				$pair = explode('":"',$value);
				$obj->$pair[0] = $pair[1];
			}//end foreach
			return $obj;
		}//end if
	}//end JSON_Decoder method
	
	/**
	*	get details from local db about product
	*	var str isbn
	*/
	public static function local_detail($product){
		$api = self::getInstance();
		$product_obj = new stdclass();
		$product_obj->isbn = $product;
		$obj = $api->_api_model_local->_parseSq1($product_obj);//lookup product in local db
		return $obj;
	}
	
	/**
	*	get details from external db about product
	*/	
	public static function external_detail($product){
		$api = self::getInstance();
		$JSONstr = $api->_api_model_service->performQuery($product);//lookup product in external db
		$obj = $api->JSON_Decoder($JSONstr);
		//var_dump($obj);
		return $obj;
	}
	
	/**
	 * Create and render a hidden input
	 * for each param group, a hidden input should be placed into the form
	 * this will make an api reference stack; we the user sends the request data
	 * we'll know which api param groups he submitted data to; we can iterate and
	 * process data accordingly
	 *
	 * @param string $control_name name of stack
	 * @param string $group_name value of stack entry
	 * @return string html hidden input element
	 */
	private static function renderHiddenParams($control_name = 'api_group', $group_name = 'bookstore_api'){
		$api = self::getInstance();
		$group_count = count($api->_api_groups);		
		$xmlobj =& self::factory('simplexml',true,'<document><params><param type="hidden" name="'.$group_count.'" /></params></document>');
		$hidden_param =& self::factory('jparam');
		$hidden_param->setXML($xmlobj->document->params[0]);		
		$hidden_param->set($group_count,$group_name);
		$api->_api_groups[$group_count] = $group;
		$node =& $hidden_param->_xml['_default']->children();
		$param_array = $hidden_param->getParam($node[0],$control_name); 
		return $param_array[1];
		//return $hidden_param->renderToArray($control_name);
	}
	
	/**
	 * Render html related to an api params group
	 * call to inject api params into html parameters on backend
	 *
	 * @param object $view JView calling view
	 * @param string $control_name variable name of array that will be returned in request
	 * @param string $group param group to render
	 * @param string $title text from parameter accordian tab
	 */
	public static function renderParams(&$view,$control_name = 'bookstore_api',$group = 'bookstore_api', $title = 'Bookstore Parameters'){		
		$api = self::getInstance();
		echo $view->pane->startPanel( JText::_($title), 'bookstore_api_params' );
		$params =& $api->factory('jparam');
		$data = $api->getXrefData($group);
		if($data){
			$params->loadObject($data);
		}
		echo $params->render($control_name,$group);		
		//register the api_group and render hidden element so it can be parsed by api when user submits info
		echo $api->renderHiddenParams('api_group',$group);			
		echo $view->pane->endPanel();
	}
	
	public static function renderView(&$view, $native_view = NULL, $group = 'bookstore_api'){
		// for frontend only NOT YET IMPLEMENTED; make your own view in xref component and use the api models
		//read 	config
		//find native view (from $native_view or as desired by xref config)
		ob_start();
		$Cconfig = array(
					'name'	=> '',
					'base_path' => JPATH_SITE.DS.'components'.DS.'com_bookstore',
						);
		$controller =& new JController($Cconfig);
		$model =& $controller->getModel();
		// something like $model->storeRequest()
		// now we can simply let the controller execute
		$controller->execute('display');
		// and hopefully all is dumped to the buffer?
		//make model
		//push model to native view		
		$native_view_output = ob_get_clean();		
		//render or expose navtive view
		return $native_view_output;
	}
	
	/**
	 * General method to get data from api_xref_data table as
	 * related to current xref component
	 *
	 * @param string $group
	 * @return mixed dependent on read$group method; FALSE on error; table(object) or rows (array of objects)
	 */
	private static function getXrefData($group = 'bookstore_api'){
		$request_option = JRequest::getString('option');
		$key = 'bookstore_api.'.$request_option.'.id';
		$primaryid = self::factory('registry')->getValue($key);
		if(!is_string($primaryid)){
			if($primaryid->datatype){
				$request_id = JRequest::getVar($primaryid->key);				
				if(is_array($request_id)){
					if($primaryid->filter && ( $primaryid->filter != 'none' ) ){
						$request_id = JFilterInput::clean($request_id[$primaryid->datatypekey], $primaryid->filter);
					}else{
						$request_id = $request_id[$primaryid->datatypekey];
					}
				}elseif(is_object($request_id)){
					$key = $primaryid->datatypekey;
					if($primaryid->filter){
						$request_id = JFilterInput::clean( $request_id->$key, $primaryid->filter);
					}else{
						$request_id = $request_id->$key;	
					}
				}
			}else{
				$request_id = JRequest::getVar($primaryid->key,null,'default',$primaryid->filter);
			}
		}else{
			$request_id = JRequest::getInt($primaryid);
		}
		//make table obj
		$dbobj->table_path = JPATH_ADMINISTRATOR.DS.'components'.DS.'com_bookstore'.DS.'tables';
		$dbobj->table_name = 'api_data_xref';
		$dbobj->table_prefix = 'Table';
		//make table data related to the xref variables
		$dbobj->data->xref_id = $request_id;
		$dbobj->data->xref_com = $request_option;
		//fetch to data array object (rows) 
		$data = self::readTable($dbobj);
		if($data === false){
			echo self::error;
		}
		$method = 'read'.ucfirst($group);
		return self::$method($data);
		
	}
	
	/**
	 * processes any api related data
	 * will iterate through the api reference stack
	 *
	 * @param array $request a POST/GET request
	 */	
	public static function storeParams($request = false,$xref_id = 0,$xref_com = null){
		$api = self::getInstance();
		$api->xref_id = $xref_id;
		$api->xref_com = $xref_com;
		$request = ($request)? $request : JRequest::get( 'post' );
		$api_groups = $request['api_group'];
		foreach($api_groups as $k=>$group_name){
			$method = 'store'.ucfirst($group_name);
			$api->$method($request);
		}
	}
	
	/**
	 * General method to write to table
	 * 
	 * optionally, your table can support a custom check() method
	 *
	 * @param object $obj object with db related info
	 * @return boolean
	 */
	private static function writeTable($obj){
		JTable::addIncludePath($obj->table_path);
		$table =& JTable::getInstance($obj->table_name,$obj->table_prefix);
		//var_dump($table);
		if (!$table->bind($obj->data)) {
			self::getInstance()->error = $table->getErrorMsg();
      		return false;
      	}	
    	if(!$table->check()) {
    		self::getInstance()->error = $table->getErrorMsg();
        	return false;
    	}
		if (!$table->store()) {
			self::getInstance()->error = $table->getErrorMsg();
        	return false;
    	}	
    	return true	;
	}
	
	/**
	 * General method to read from db
	 * 
	 * NOTE: your db table MUST implement a locateData method
	 * which takes an data object
	 *
	 * @param object $obj object with db related info
	 * @return mixed FALSE on error; table(object) or rows (array of objects)
	 */
	private static function readTable($obj){
		JTable::addIncludePath($obj->table_path);
		$table =& JTable::getInstance($obj->table_name,$obj->table_prefix);
		if($obj->data->id){//id specificed			
			if (!$table->load($obj->data->id)) {
				self::getInstance()->error = $table->getErrorMsg();
      			return false;
      		}		
      		return $table;
		}else{//no id, make query			
			$rows =& $table->locateData($obj->data);
			if(!rows){
				self::getInstance()->error = $table->getErrorMsg();
      			return false;
      		}
      		return $rows;			
		}
	}//end readTable
	
	/**
	 * translate group 'bookstore_api' specific data from api_xref_data rows
	 * 
	 * translates or 'maps' db info so it will match params file/object
	 *
	 * @param mixed $rows array object or object
	 * @return object mapped object
	 */
	private static function readBookstore_api($rows){
		if(is_array($rows)){
			foreach($rows as $k=>$row){	
				$params = JRegistry::getInstance('tmp');
				$params->loadINI($row->params);
				$data[$k] = $params->toObject();
				$data[$k]->assoc_isbn = $row->native_id;
				$data[$k]->api_xref_id = $row->id;
			}
			return $data[0];
		}else{
			$params = JRegistry::getInstance('tmp');
			$params->loadINI($row->params);
			$data = $params->toObject();
			$data->assoc_isbn = $row->native_id;
			$data->api_xref_id = $row->id;
			return $data;
		}
	}
	
	/**
	 * Method to filter and store group 'bookstore_api'
	 *
	 * @param array $request POST/GET request array
	 */
	private static function storeBookstore_api($request){
		
		$api = self::getInstance();
		$name = 'bookstore_api';
		$data =& $request[$name];
		if( empty($data['assoc_isbn']) ){return;}
		//sanitize input according to xml params file
		
		$xmlobj = clone self::factory('simplexml',false); //done to reduce file reading
		foreach($xmlobj->document->params as $k=>$param_set){
			if($param_set->attributes('group') == $name){
				foreach($xmlobj->document->params[$k]->children() as $param){
					$name_attr = $param->attributes('name');
					$data[$name_attr] = trim( JFilterInput::clean($data[$name_attr], $param->attributes('filter')) );	
				}
				
			}
		}
			
		//magically fetch image path (from external info service)		
		if($data['update_path']){
			$external = $api->external_detail($data['assoc_isbn']);
			$graphic_path = $external->graphics;
			if(strpos($graphic_path,'http://') === 0){				
				$data['img_path'] = 'http://'.JFilterInput::clean(substr($graphic_path,7),'PATH');
			}else{
				$data['img_path'] = JFilterInput::clean($graphic_path,'PATH');
			}			
			$data['update_path'] = 0;
		}
		
		//make our db data obj
		$dbobj->table_path = JPATH_ADMINISTRATOR.DS.'components'.DS.'com_bookstore'.DS.'tables';
		$dbobj->table_name = 'api_data_xref';
		$dbobj->table_prefix = 'Table';
		$id = (empty($data['api_xref_id']))? NULL : $data['api_xref_id'];
		unset($data['api_xref_id']);
		$assoc_isbn = $data['assoc_isbn'];
		unset($data['assoc_isbn']);
		$params_array = JRegistry::getInstance('api_data_xref.bookstore_api');
		$params_array->loadArray($data);		
		$dbobj->data = array(
							'id'		=> $id,
							'native_id'	=> $assoc_isbn,
							'xref_id'	=> $api->xref_id,
							'native_com'=> 'com_bookstore',
							'xref_com'	=> $api->xref_com,
							'params'	=> $params_array->toString('INI')
						);
		//store to db
		if(!$api->writeTable($dbobj)){
			die($api->error);
		}
		//register this group
		$api->_api_groups[$name] = '';
		
	}//end storeBookstore_api method
	
	//TODO: evaluate magic methods, try to do away with this recursion
	///////////////////////////////////////////
	/////////// magic methods /////////////////
	///////////////////////////////////////////
	function __get($varname){ 
		$altname = '_'.$varname; 
		if(isset($this->$varname)){
			return $this->$varname;
		}elseif(isset($this->$altname)){
			return $this->altname;
		}else{
			return $this->_vars[$varname];
		}
	}
	function __set($varname,$varvalue){
		$altname = '_'.$varname; 
		if(isset($this->$varname)){
			$this->$varname = $varvalue;
		}elseif(isset($this->$altname)){
			$this->altname = $varvalue;
		}else{
			self::getInstance()->_vars[$varname] = $varvalue;
		}
		return $this;
	}	
	//function __unset($varname){	unset(self::getInstance()->_vars[$varname]); }	
	//function __call($method,$args){	$methodname = '_'.$method; self::getInstance()->$methodname( implode(',',$args) ); }
}//end bookstore_api
?>